import os
import ast
import re
from PySide6.QtWidgets import (
    QApplication, QWidget, QVBoxLayout, QPushButton, QFileDialog,
    QTextEdit, QLabel, QLineEdit, QHBoxLayout, QGroupBox,
    QProgressBar, QMessageBox, QComboBox, QListWidget, QDialog,
    QDialogButtonBox, QFormLayout, QListWidgetItem, QTabWidget,
    QCheckBox
)
from PySide6.QtCore import Qt, QThread, Signal
from PySide6.QtGui import QFont

class FileTreeDialog(QDialog):
    def __init__(self, tree_text, parent=None):
        super().__init__(parent)
        self.setWindowTitle("文件树状图")
        self.resize(800, 600)
        
        layout = QVBoxLayout()
        
        # 标题
        title_label = QLabel("符合条件的文件树状图")
        title_label.setAlignment(Qt.AlignCenter)
        title_font = QFont()
        title_font.setPointSize(14)
        title_font.setBold(True)
        title_label.setFont(title_font)
        layout.addWidget(title_label)
        
        # 树状图显示
        self.tree_text = QTextEdit()
        self.tree_text.setPlainText(tree_text)
        self.tree_text.setReadOnly(True)
        layout.addWidget(self.tree_text)
        
        # 按钮
        button_layout = QHBoxLayout()
        self.copy_btn = QPushButton("复制到剪贴板")
        self.copy_btn.clicked.connect(self.copy_to_clipboard)
        
        self.save_btn = QPushButton("保存为文件")
        self.save_btn.clicked.connect(self.save_to_file)
        
        self.close_btn = QPushButton("关闭")
        self.close_btn.clicked.connect(self.accept)
        
        button_layout.addWidget(self.copy_btn)
        button_layout.addWidget(self.save_btn)
        button_layout.addWidget(self.close_btn)
        layout.addLayout(button_layout)
        
        self.setLayout(layout)
    
    def copy_to_clipboard(self):
        clipboard = QApplication.clipboard()
        clipboard.setText(self.tree_text.toPlainText())
        QMessageBox.information(self, "成功", "树状图已复制到剪贴板！")
    
    def save_to_file(self):
        file_path, _ = QFileDialog.getSaveFileName(
            self, "保存树状图", "file_tree.txt", 
            "Text Files (*.txt);;All Files (*)"
        )
        if file_path:
            try:
                with open(file_path, "w", encoding="utf-8") as f:
                    f.write(self.tree_text.toPlainText())
                QMessageBox.information(self, "成功", f"树状图已保存到: {file_path}")
            except Exception as e:
                QMessageBox.critical(self, "错误", f"保存文件时出错: {str(e)}")

class PresetDialog(QDialog):
    def __init__(self, presets, parent=None):
        super().__init__(parent)
        self.presets = presets
        self.setWindowTitle("管理预设")
        self.resize(500, 400)
        
        layout = QVBoxLayout()
        
        # 预设列表
        layout.addWidget(QLabel("预设列表:"))
        self.preset_list = QListWidget()
        self.update_preset_list()
        layout.addWidget(self.preset_list)
        
        # 按钮
        button_layout = QHBoxLayout()
        self.add_btn = QPushButton("添加预设")
        self.edit_btn = QPushButton("编辑预设")
        self.delete_btn = QPushButton("删除预设")
        self.add_btn.clicked.connect(self.add_preset)
        self.edit_btn.clicked.connect(self.edit_preset)
        self.delete_btn.clicked.connect(self.delete_preset)
        
        button_layout.addWidget(self.add_btn)
        button_layout.addWidget(self.edit_btn)
        button_layout.addWidget(self.delete_btn)
        layout.addLayout(button_layout)
        
        # 对话框按钮
        self.button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
        self.button_box.accepted.connect(self.accept)
        self.button_box.rejected.connect(self.reject)
        layout.addWidget(self.button_box)
        
        self.setLayout(layout)
    
    def update_preset_list(self):
        self.preset_list.clear()
        for name in self.presets.keys():
            self.preset_list.addItem(name)
    
    def add_preset(self):
        dialog = PresetEditDialog(self.presets, self)
        if dialog.exec() == QDialog.Accepted:
            name, extensions = dialog.get_preset_data()
            if name and name not in self.presets:
                self.presets[name] = extensions
                self.update_preset_list()
    
    def edit_preset(self):
        current_item = self.preset_list.currentItem()
        if not current_item:
            QMessageBox.warning(self, "警告", "请选择一个预设进行编辑")
            return
        
        preset_name = current_item.text()
        dialog = PresetEditDialog(self.presets, self, preset_name)
        if dialog.exec() == QDialog.Accepted:
            new_name, extensions = dialog.get_preset_data()
            if new_name != preset_name and new_name in self.presets:
                QMessageBox.warning(self, "警告", "预设名称已存在")
                return
            
            # 删除旧的，添加新的
            if new_name != preset_name:
                del self.presets[preset_name]
            self.presets[new_name] = extensions
            self.update_preset_list()
    
    def delete_preset(self):
        current_item = self.preset_list.currentItem()
        if not current_item:
            QMessageBox.warning(self, "警告", "请选择要删除的预设")
            return
        
        preset_name = current_item.text()
        reply = QMessageBox.question(
            self, "确认删除", 
            f"确定要删除预设 '{preset_name}' 吗？",
            QMessageBox.Yes | QMessageBox.No
        )
        
        if reply == QMessageBox.Yes:
            del self.presets[preset_name]
            self.update_preset_list()

class PresetEditDialog(QDialog):
    def __init__(self, presets, parent=None, preset_name=None):
        super().__init__(parent)
        self.presets = presets
        self.preset_name = preset_name
        
        self.setWindowTitle("编辑预设" if preset_name else "添加预设")
        self.resize(400, 300)
        
        layout = QFormLayout()
        
        # 预设名称
        self.name_input = QLineEdit()
        if preset_name:
            self.name_input.setText(preset_name)
        layout.addRow("预设名称:", self.name_input)
        
        # 常见扩展名选择
        layout.addRow(QLabel("常用文件类型:"))
        
        # 预定义的常见文件类型
        self.common_extensions = {
            "Python编译文件": [".pyc", ".pyo", "__pycache__"],
            "Python包文件": [".egg", ".whl", "dist/", "build/"],
            "C++编译文件": [".obj", ".o", ".exe", ".dll", ".lib", ".so", ".a"],
            "C++构建文件": ["CMakeFiles/", "cmake_install.cmake", "CMakeCache.txt", "Makefile"],
            "Java编译文件": [".class", ".jar"],
            "日志文件": [".log", ".tmp", ".temp"],
            "IDE文件": [".vscode/", ".idea/", "*.swp", "*.swo"],
            "版本控制": [".git/", ".svn/"],
            "系统文件": [".DS_Store", "Thumbs.db"]
        }
        
        for category, exts in self.common_extensions.items():
            checkbox = QCheckBox(category)
            checkbox.extensions = exts
            checkbox.toggled.connect(self.on_category_toggled)
            layout.addRow(checkbox)
        
        # 自定义扩展名
        layout.addRow(QLabel("自定义文件类型（每行一个）:"))
        self.custom_input = QTextEdit()
        self.custom_input.setMaximumHeight(100)
        if preset_name and preset_name in self.presets:
            # 显示已有的自定义设置
            existing_exts = self.presets[preset_name]
            custom_text = "\n".join([ext for ext in existing_exts if not any(ext in common for common in self.common_extensions.values())])
            self.custom_input.setPlainText(custom_text)
        layout.addRow(self.custom_input)
        
        # 按钮
        self.button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
        self.button_box.accepted.connect(self.accept)
        self.button_box.rejected.connect(self.reject)
        layout.addRow(self.button_box)
        
        self.setLayout(layout)
    
    def on_category_toggled(self, checked):
        # 这里可以实时更新预览，但为了简单起见，我们只在确定时处理
        pass
    
    def get_preset_data(self):
        name = self.name_input.text().strip()
        
        # 收集所有选中的扩展名
        extensions = []
        
        # 从复选框收集
        for checkbox in self.findChildren(QCheckBox):
            if checkbox.isChecked():
                extensions.extend(checkbox.extensions)
        
        # 从自定义输入收集
        custom_text = self.custom_input.toPlainText().strip()
        if custom_text:
            custom_exts = [line.strip() for line in custom_text.split('\n') if line.strip()]
            extensions.extend(custom_exts)
        
        return name, extensions

class ScanThread(QThread):
    """扫描线程，避免界面卡顿"""
    progress_signal = Signal(int)
    finished_signal = Signal(str, str)  # 现在返回两个字符串：文件内容和树状图
    log_signal = Signal(str)  # 添加日志信号
    
    def __init__(self, root_dir, ignore_extensions):
        super().__init__()
        self.root_dir = root_dir
        self.ignore_extensions = ignore_extensions
        self.scanned_files = set()  # 用于跟踪已扫描的文件，避免重复
        
    def run(self):
        result_text = []
        tree_structure = {}
        total_files = 0
        processed_files = 0
        
        # 先统计文件总数
        self.log_signal.emit("正在统计文件总数...")
        for dirpath, dirnames, filenames in os.walk(self.root_dir):
            # 过滤目录
            dirnames[:] = [d for d in dirnames if not self.should_ignore(d, dirpath, is_dir=True)]
            
            for filename in filenames:
                if not self.should_ignore(filename, dirpath):
                    total_files += 1
        
        self.log_signal.emit(f"找到 {total_files} 个文件需要处理")
        
        # 开始扫描文件
        for dirpath, dirnames, filenames in os.walk(self.root_dir):
            # 过滤目录
            dirnames[:] = [d for d in dirnames if not self.should_ignore(d, dirpath, is_dir=True)]
            filenames.sort()
            
            for filename in filenames:
                if self.should_ignore(filename, dirpath):
                    continue

                file_path = os.path.join(dirpath, filename)
                
                # 检查文件是否已经扫描过（避免重复）
                file_id = os.path.abspath(file_path)
                if file_id in self.scanned_files:
                    continue
                self.scanned_files.add(file_id)

                relative_path = os.path.relpath(file_path, self.root_dir)
                
                # 添加到文件内容结果
                result_text.append(f"=====FILE_START: {relative_path}=====")
                try:
                    with open(file_path, "r", encoding="utf-8", errors='ignore') as f:
                        content = f.read()
                        result_text.append(content)
                except Exception as e:
                    error_msg = f"// 无法读取文件: {e}"
                    result_text.append(error_msg)
                    self.log_signal.emit(f"无法读取文件 {relative_path}: {e}")
                
                result_text.append(f"=====FILE_END=====\n")
                
                # 构建树状结构
                self.add_to_tree_structure(tree_structure, relative_path)
                
                processed_files += 1
                progress = int((processed_files / total_files) * 100) if total_files > 0 else 0
                self.progress_signal.emit(progress)
                self.log_signal.emit(f"已处理 {processed_files}/{total_files} 个文件: {relative_path}")
        
        final_text = "\n".join(result_text)
        tree_text = self.generate_tree_text(tree_structure, os.path.basename(self.root_dir.rstrip(os.sep)))
        self.log_signal.emit(f"扫描完成！共处理 {processed_files} 个文件")
        self.finished_signal.emit(final_text, tree_text)
    
    def should_ignore(self, name, dirpath, is_dir=False):
        """检查文件或目录是否应该被忽略"""
        full_path = os.path.join(dirpath, name)
        relative_path = os.path.relpath(full_path, self.root_dir)
        
        # 对于目录，检查目录名和完整路径
        if is_dir:
            # 检查目录名是否匹配任何屏蔽模式
            for pattern in self.ignore_extensions:
                pattern = pattern.strip()
                if not pattern:
                    continue
                    
                # 处理目录模式（以 / 结尾）
                if pattern.endswith('/'):
                    dir_pattern = pattern.rstrip('/')
                    # 检查目录名是否匹配
                    if name == dir_pattern:
                        return True
                    # 检查相对路径是否以模式开头
                    if relative_path.replace('\\', '/').startswith(dir_pattern + '/'):
                        return True
                # 处理通配符模式
                elif pattern.startswith('*'):
                    ext = pattern[1:]
                    if name.endswith(ext):
                        return True
                # 处理普通模式
                else:
                    if name == pattern or name.endswith(pattern):
                        return True
            return False
        
        # 对于文件
        else:
            for pattern in self.ignore_extensions:
                pattern = pattern.strip()
                if not pattern:
                    continue
                    
                # 处理目录模式（文件不应该匹配目录模式）
                if pattern.endswith('/'):
                    continue
                    
                # 处理通配符模式
                if pattern.startswith('*'):
                    ext = pattern[1:]
                    if name.endswith(ext):
                        return True
                # 处理普通模式
                else:
                    if name == pattern or name.endswith(pattern):
                        return True
            return False
    
    def add_to_tree_structure(self, tree, relative_path):
        """将文件路径添加到树状结构中"""
        parts = relative_path.split(os.sep)
        current_level = tree
        
        for i, part in enumerate(parts):
            if i == len(parts) - 1:  # 文件
                if 'files' not in current_level:
                    current_level['files'] = []
                if part not in current_level['files']:  # 避免重复添加
                    current_level['files'].append(part)
            else:  # 目录
                if part not in current_level:
                    current_level[part] = {}
                current_level = current_level[part]
    
    def generate_tree_text(self, tree, root_name, prefix="", is_last=True):
        """生成树状图文本"""
        lines = []
        
        if prefix == "":  # 根目录
            lines.append(f"{root_name}/")
        else:
            connector = "└── " if is_last else "├── "
            lines.append(f"{prefix}{connector}{root_name}/")
        
        # 处理子目录
        dirs = [k for k in tree.keys() if k != 'files']
        dirs.sort()
        
        for i, dir_name in enumerate(dirs):
            is_last_dir = (i == len(dirs) - 1) and ('files' not in tree or len(tree['files']) == 0)
            new_prefix = prefix + ("    " if is_last else "│   ")
            dir_text = self.generate_tree_text(tree[dir_name], dir_name, new_prefix, is_last_dir)
            lines.append(dir_text)
        
        # 处理文件
        if 'files' in tree:
            files = sorted(tree['files'])
            for i, file_name in enumerate(files):
                is_last_file = i == len(files) - 1
                connector = "└── " if is_last_file else "├── "
                lines.append(f"{prefix}{connector}{file_name}")
        
        return "\n".join(lines)

class APIAnalyzerThread(QThread):
    """API分析线程，避免界面卡顿"""
    progress_signal = Signal(int)
    log_signal = Signal(str)
    finished_signal = Signal(dict)
    
    def __init__(self, project_dir, language, include_functions, include_classes, include_methods, include_docstrings):
        super().__init__()
        self.project_dir = project_dir
        self.language = language
        self.include_functions = include_functions
        self.include_classes = include_classes
        self.include_methods = include_methods
        self.include_docstrings = include_docstrings
        
    def run(self):
        try:
            self.log_signal.emit(f"开始分析项目: {self.project_dir}")
            api_data = self.analyze_project()
            self.finished_signal.emit(api_data)
        except Exception as e:
            self.log_signal.emit(f"分析过程中出错: {str(e)}")
            self.finished_signal.emit({"error": str(e)})
    
    def analyze_project(self):
        """分析项目结构，提取API信息"""
        project_name = os.path.basename(self.project_dir)
        api_data = {
            "project_name": project_name,
            "modules": []
        }
        
        # 获取所有源代码文件
        code_files = self.get_code_files()
        total_files = len(code_files)
        
        if total_files == 0:
            self.log_signal.emit("未找到源代码文件")
            return api_data
        
        self.log_signal.emit(f"找到 {total_files} 个源代码文件")
        
        # 分析每个文件
        for i, file_path in enumerate(code_files):
            relative_path = os.path.relpath(file_path, self.project_dir)
            self.log_signal.emit(f"分析文件: {relative_path}")
            
            module_data = self.analyze_file(file_path)
            if module_data:
                api_data["modules"].append(module_data)
            
            # 更新进度
            progress = int((i + 1) / total_files * 100)
            self.progress_signal.emit(progress)
        
        self.log_signal.emit("项目分析完成")
        return api_data
    
    def get_code_files(self):
        """获取项目中的源代码文件"""
        code_extensions = {
            "Python": [".py"],
            "C++": [".cpp", ".h", ".hpp", ".c", ".cc"],
            "Java": [".java"],
            "JavaScript": [".js"],
            "TypeScript": [".ts", ".tsx"]
        }
        
        # 确定要搜索的扩展名
        if self.language == "自动检测":
            extensions = []
            for lang_exts in code_extensions.values():
                extensions.extend(lang_exts)
        else:
            extensions = code_extensions.get(self.language, [".py"])
        
        # 收集文件
        code_files = []
        for root, dirs, files in os.walk(self.project_dir):
            # 跳过一些常见的不需要分析的目录
            skip_dirs = [".git", "__pycache__", "node_modules", "build", "dist", ".idea", ".vscode"]
            dirs[:] = [d for d in dirs if d not in skip_dirs]
            
            for file in files:
                if any(file.endswith(ext) for ext in extensions):
                    code_files.append(os.path.join(root, file))
        
        return code_files
    
    def analyze_file(self, file_path):
        """分析单个文件"""
        file_ext = os.path.splitext(file_path)[1].lower()
        
        if file_ext == ".py":
            return self.analyze_python_file(file_path)
        elif file_ext in [".cpp", ".h", ".hpp", ".c", ".cc"]:
            return self.analyze_cpp_file(file_path)
        elif file_ext == ".java":
            return self.analyze_java_file(file_path)
        elif file_ext == ".js":
            return self.analyze_javascript_file(file_path)
        elif file_ext in [".ts", ".tsx"]:
            return self.analyze_typescript_file(file_path)
        else:
            return None
    
    def analyze_python_file(self, file_path):
        """分析Python文件"""
        try:
            with open(file_path, "r", encoding="utf-8") as f:
                content = f.read()
            
            module_name = os.path.basename(file_path)
            module_data = {
                "name": module_name,
                "functions": [],
                "classes": []
            }
            
            # 使用AST解析Python代码
            tree = ast.parse(content)
            
            for node in ast.walk(tree):
                # 提取函数定义
                if self.include_functions and isinstance(node, ast.FunctionDef):
                    function_data = self.extract_python_function(node)
                    if function_data:
                        module_data["functions"].append(function_data)
                
                # 提取类定义
                elif self.include_classes and isinstance(node, ast.ClassDef):
                    class_data = self.extract_python_class(node)
                    if class_data:
                        module_data["classes"].append(class_data)
            
            # 如果没有任何内容，返回None
            if not module_data["functions"] and not module_data["classes"]:
                return None
                
            return module_data
            
        except Exception as e:
            self.log_signal.emit(f"解析Python文件 {file_path} 时出错: {str(e)}")
            return None
    
    def extract_python_function(self, node):
        """提取Python函数信息"""
        # 获取函数名
        func_name = node.name
        
        # 跳过私有函数（以_开头）
        if func_name.startswith('_') and not func_name.startswith('__'):
            return None
        
        # 获取参数
        args = []
        for arg in node.args.args:
            arg_name = arg.arg
            if arg_name != 'self':  # 跳过self参数
                args.append(arg_name)
        
        # 获取返回类型注解
        return_type = None
        if node.returns:
            if isinstance(node.returns, ast.Name):
                return_type = node.returns.id
            elif isinstance(node.returns, ast.Attribute):
                return_type = ast.unparse(node.returns) if hasattr(ast, 'unparse') else str(node.returns)
            elif isinstance(node.returns, ast.Subscript):
                return_type = ast.unparse(node.returns) if hasattr(ast, 'unparse') else str(node.returns)
        
        # 获取文档字符串
        docstring = None
        if (node.body and isinstance(node.body[0], ast.Expr) and 
            isinstance(node.body[0].value, ast.Str)):
            docstring = node.body[0].value.s
        
        # 构建参数字符串
        args_str = ", ".join(args)
        
        return {
            "name": func_name,
            "args": args_str,
            "return_type": return_type,
            "docstring": docstring
        }
    
    def extract_python_class(self, node):
        """提取Python类信息"""
        # 获取类名
        class_name = node.name
        
        # 跳过私有类（以_开头）
        if class_name.startswith('_') and not class_name.startswith('__'):
            return None
        
        class_data = {
            "name": class_name,
            "methods": [],
            "docstring": None
        }
        
        # 获取类的文档字符串
        if (node.body and isinstance(node.body[0], ast.Expr) and 
            isinstance(node.body[0].value, ast.Str)):
            class_data["docstring"] = node.body[0].value.s
        
        # 提取类方法
        if self.include_methods:
            for item in node.body:
                if isinstance(item, ast.FunctionDef):
                    method_data = self.extract_python_function(item)
                    if method_data:
                        class_data["methods"].append(method_data)
        
        return class_data
    
    def analyze_cpp_file(self, file_path):
        """分析C++文件"""
        try:
            with open(file_path, "r", encoding="utf-8") as f:
                content = f.read()
            
            module_name = os.path.basename(file_path)
            module_data = {
                "name": module_name,
                "functions": [],
                "classes": []
            }
            
            # 使用正则表达式提取C++函数
            if self.include_functions:
                # 匹配函数定义的正则表达式
                function_pattern = r'(?:(\w+)\s+)?(\w+)\s*\(([^)]*)\)\s*(?:const)?\s*\{'
                functions = re.findall(function_pattern, content)
                
                for return_type, func_name, args in functions:
                    # 跳过一些明显不是函数的情况
                    if func_name in ['if', 'for', 'while', 'switch']:
                        continue
                    
                    function_data = {
                        "name": func_name,
                        "args": args.strip(),
                        "return_type": return_type if return_type else "void",
                        "docstring": None
                    }
                    module_data["functions"].append(function_data)
            
            # 使用正则表达式提取C++类
            if self.include_classes:
                # 匹配类定义的正则表达式
                class_pattern = r'class\s+(\w+)\s*(?::\s*\w+\s*)?\{'
                classes = re.findall(class_pattern, content)
                
                for class_name in classes:
                    class_data = {
                        "name": class_name,
                        "methods": [],
                        "docstring": None
                    }
                    
                    # 提取类方法
                    if self.include_methods:
                        # 在类定义范围内查找方法
                        class_body_pattern = r'class\s+' + class_name + r'\s*[^\{]*\{([^\}]+)\}'
                        class_body_match = re.search(class_body_pattern, content, re.DOTALL)
                        
                        if class_body_match:
                            class_body = class_body_match.group(1)
                            # 在类体内查找方法定义
                            method_pattern = r'(\w+)\s+(\w+)\s*\(([^)]*)\)\s*(?:const)?\s*[;{]'
                            methods = re.findall(method_pattern, class_body)
                            
                            for return_type, method_name, args in methods:
                                method_data = {
                                    "name": method_name,
                                    "args": args.strip(),
                                    "return_type": return_type,
                                    "docstring": None
                                }
                                class_data["methods"].append(method_data)
                    
                    module_data["classes"].append(class_data)
            
            # 如果没有任何内容，返回None
            if not module_data["functions"] and not module_data["classes"]:
                return None
                
            return module_data
            
        except Exception as e:
            self.log_signal.emit(f"解析C++文件 {file_path} 时出错: {str(e)}")
            return None
    
    def analyze_java_file(self, file_path):
        """分析Java文件"""
        try:
            with open(file_path, "r", encoding="utf-8") as f:
                content = f.read()
            
            module_name = os.path.basename(file_path)
            module_data = {
                "name": module_name,
                "functions": [],
                "classes": []
            }
            
            # 使用正则表达式提取Java函数/方法
            if self.include_functions:
                # 匹配Java方法定义的正则表达式
                method_pattern = r'(?:public|private|protected)?\s*(?:static)?\s*(\w+(?:<\w+>)?)\s+(\w+)\s*\(([^)]*)\)'
                methods = re.findall(method_pattern, content)
                
                for return_type, method_name, args in methods:
                    # 跳过构造函数（方法名与类名相同的情况）
                    if method_name == os.path.splitext(module_name)[0]:
                        continue
                    
                    function_data = {
                        "name": method_name,
                        "args": args.strip(),
                        "return_type": return_type,
                        "docstring": None
                    }
                    module_data["functions"].append(function_data)
            
            # 使用正则表达式提取Java类
            if self.include_classes:
                # 匹配类定义的正则表达式
                class_pattern = r'class\s+(\w+)\s*(?:extends\s+\w+)?\s*(?:implements\s+[^{]+)?\{'
                classes = re.findall(class_pattern, content)
                
                for class_name in classes:
                    class_data = {
                        "name": class_name,
                        "methods": [],
                        "docstring": None
                    }
                    
                    # 提取类方法
                    if self.include_methods:
                        # 在类定义范围内查找方法
                        class_body_pattern = r'class\s+' + class_name + r'\s*[^\{]*\{([^\}]+)\}'
                        class_body_match = re.search(class_body_pattern, content, re.DOTALL)
                        
                        if class_body_match:
                            class_body = class_body_match.group(1)
                            # 在类体内查找方法定义
                            method_pattern = r'(?:public|private|protected)?\s*(?:static)?\s*(\w+(?:<\w+>)?)\s+(\w+)\s*\(([^)]*)\)'
                            methods = re.findall(method_pattern, class_body)
                            
                            for return_type, method_name, args in methods:
                                # 跳过构造函数
                                if method_name != class_name:
                                    method_data = {
                                        "name": method_name,
                                        "args": args.strip(),
                                        "return_type": return_type,
                                        "docstring": None
                                    }
                                    class_data["methods"].append(method_data)
                    
                    module_data["classes"].append(class_data)
            
            # 如果没有任何内容，返回None
            if not module_data["functions"] and not module_data["classes"]:
                return None
                
            return module_data
            
        except Exception as e:
            self.log_signal.emit(f"解析Java文件 {file_path} 时出错: {str(e)}")
            return None
    
    def analyze_javascript_file(self, file_path):
        """分析JavaScript文件"""
        try:
            with open(file_path, "r", encoding="utf-8") as f:
                content = f.read()
            
            module_name = os.path.basename(file_path)
            module_data = {
                "name": module_name,
                "functions": [],
                "classes": []
            }
            
            # 使用正则表达式提取JavaScript函数
            if self.include_functions:
                # 匹配函数定义的正则表达式
                function_pattern = r'function\s+(\w+)\s*\(([^)]*)\)\s*\{'
                functions = re.findall(function_pattern, content)
                
                for func_name, args in functions:
                    function_data = {
                        "name": func_name,
                        "args": args.strip(),
                        "return_type": None,  # JavaScript没有明确的返回类型
                        "docstring": None
                    }
                    module_data["functions"].append(function_data)
                
                # 匹配箭头函数和const/let/var函数
                arrow_function_pattern = r'(?:const|let|var)\s+(\w+)\s*=\s*(?:\(([^)]*)\)|\w+)\s*=>'
                arrow_functions = re.findall(arrow_function_pattern, content)
                
                for func_name, args in arrow_functions:
                    function_data = {
                        "name": func_name,
                        "args": args.strip(),
                        "return_type": None,
                        "docstring": None
                    }
                    module_data["functions"].append(function_data)
            
            # 使用正则表达式提取JavaScript类
            if self.include_classes:
                # 匹配类定义的正则表达式
                class_pattern = r'class\s+(\w+)\s*(?:extends\s+\w+)?\s*\{'
                classes = re.findall(class_pattern, content)
                
                for class_name in classes:
                    class_data = {
                        "name": class_name,
                        "methods": [],
                        "docstring": None
                    }
                    
                    # 提取类方法
                    if self.include_methods:
                        # 在类定义范围内查找方法
                        class_body_pattern = r'class\s+' + class_name + r'\s*[^\{]*\{([^\}]+)\}'
                        class_body_match = re.search(class_body_pattern, content, re.DOTALL)
                        
                        if class_body_match:
                            class_body = class_body_match.group(1)
                            # 在类体内查找方法定义
                            method_pattern = r'(\w+)\s*\(([^)]*)\)\s*\{'
                            methods = re.findall(method_pattern, class_body)
                            
                            for method_name, args in methods:
                                # 跳过constructor
                                if method_name != 'constructor':
                                    method_data = {
                                        "name": method_name,
                                        "args": args.strip(),
                                        "return_type": None,
                                        "docstring": None
                                    }
                                    class_data["methods"].append(method_data)
                    
                    module_data["classes"].append(class_data)
            
            # 如果没有任何内容，返回None
            if not module_data["functions"] and not module_data["classes"]:
                return None
                
            return module_data
            
        except Exception as e:
            self.log_signal.emit(f"解析JavaScript文件 {file_path} 时出错: {str(e)}")
            return None
    
    def analyze_typescript_file(self, file_path):
        """分析TypeScript文件"""
        try:
            with open(file_path, "r", encoding="utf-8") as f:
                content = f.read()
            
            module_name = os.path.basename(file_path)
            module_data = {
                "name": module_name,
                "functions": [],
                "classes": []
            }
            
            # 使用正则表达式提取TypeScript函数
            if self.include_functions:
                # 匹配函数定义的正则表达式（包含类型注解）
                function_pattern = r'function\s+(\w+)\s*\(([^)]*)\)\s*(?::\s*\w+(?:\s*<\s*\w+\s*>)?)?\s*\{'
                functions = re.findall(function_pattern, content)
                
                for func_name, args in functions:
                    function_data = {
                        "name": func_name,
                        "args": args.strip(),
                        "return_type": None,  # 暂时不解析返回类型
                        "docstring": None
                    }
                    module_data["functions"].append(function_data)
                
                # 匹配箭头函数和const/let/var函数
                arrow_function_pattern = r'(?:const|let|var)\s+(\w+)\s*=\s*(?:\(([^)]*)\)|\w+)\s*:\s*\w+\s*=>'
                arrow_functions = re.findall(arrow_function_pattern, content)
                
                for func_name, args in arrow_functions:
                    function_data = {
                        "name": func_name,
                        "args": args.strip(),
                        "return_type": None,
                        "docstring": None
                    }
                    module_data["functions"].append(function_data)
            
            # 使用正则表达式提取TypeScript类
            if self.include_classes:
                # 匹配类定义的正则表达式
                class_pattern = r'class\s+(\w+)\s*(?:extends\s+\w+)?\s*(?:implements\s+[^{]+)?\{'
                classes = re.findall(class_pattern, content)
                
                for class_name in classes:
                    class_data = {
                        "name": class_name,
                        "methods": [],
                        "docstring": None
                    }
                    
                    # 提取类方法
                    if self.include_methods:
                        # 在类定义范围内查找方法
                        class_body_pattern = r'class\s+' + class_name + r'\s*[^\{]*\{([^\}]+)\}'
                        class_body_match = re.search(class_body_pattern, content, re.DOTALL)
                        
                        if class_body_match:
                            class_body = class_body_match.group(1)
                            # 在类体内查找方法定义
                            method_pattern = r'(\w+)\s*\(([^)]*)\)\s*(?::\s*\w+)?\s*\{'
                            methods = re.findall(method_pattern, class_body)
                            
                            for method_name, args in methods:
                                # 跳过constructor
                                if method_name != 'constructor':
                                    method_data = {
                                        "name": method_name,
                                        "args": args.strip(),
                                        "return_type": None,
                                        "docstring": None
                                    }
                                    class_data["methods"].append(method_data)
                    
                    module_data["classes"].append(class_data)
            
            # 如果没有任何内容，返回None
            if not module_data["functions"] and not module_data["classes"]:
                return None
                
            return module_data
            
        except Exception as e:
            self.log_signal.emit(f"解析TypeScript文件 {file_path} 时出错: {str(e)}")
            return None

class APIOverviewTab(QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.parent = parent
        self.analyzer_thread = None
        self.setup_ui()
    
    def setup_ui(self):
        layout = QVBoxLayout()
        
        # 标题
        title_label = QLabel("项目API概览生成器")
        title_label.setAlignment(Qt.AlignCenter)
        title_font = QFont()
        title_font.setPointSize(14)
        title_font.setBold(True)
        title_label.setFont(title_font)
        title_label.setStyleSheet("color: #2c3e50; margin-bottom: 10px;")
        layout.addWidget(title_label)
        
        # 说明文字
        description = QLabel(
            "此功能自动提取项目中的函数和类定义，生成API文档。\n"
            "您可以将生成的文档提供给AI助手，以便更好地理解项目结构并提供帮助。"
        )
        description.setWordWrap(True)
        description.setStyleSheet("color: #666; background-color: #f8f9fa; padding: 10px; border-radius: 5px;")
        layout.addWidget(description)
        
        # 设置分组
        settings_group = QGroupBox("分析设置")
        settings_layout = QVBoxLayout()
        
        # 项目目录选择
        dir_layout = QHBoxLayout()
        dir_layout.addWidget(QLabel("项目目录:"))
        self.dir_input = QLineEdit()
        self.dir_input.setPlaceholderText("请选择项目根目录...")
        dir_layout.addWidget(self.dir_input)
        self.dir_btn = QPushButton("浏览")
        self.dir_btn.clicked.connect(self.choose_project_dir)
        dir_layout.addWidget(self.dir_btn)
        settings_layout.addLayout(dir_layout)
        
        # 语言选择
        lang_layout = QHBoxLayout()
        lang_layout.addWidget(QLabel("主要语言:"))
        self.lang_combo = QComboBox()
        self.lang_combo.addItems(["自动检测", "Python", "C++", "Java", "JavaScript", "TypeScript"])
        lang_layout.addWidget(self.lang_combo)
        settings_layout.addLayout(lang_layout)
        
        # 分析选项
        options_layout = QHBoxLayout()
        self.include_functions = QCheckBox("包含函数")
        self.include_functions.setChecked(True)
        self.include_classes = QCheckBox("包含类")
        self.include_classes.setChecked(True)
        self.include_methods = QCheckBox("包含类方法")
        self.include_methods.setChecked(True)
        self.include_docstrings = QCheckBox("包含文档字符串")
        self.include_docstrings.setChecked(True)
        
        options_layout.addWidget(self.include_functions)
        options_layout.addWidget(self.include_classes)
        options_layout.addWidget(self.include_methods)
        options_layout.addWidget(self.include_docstrings)
        options_layout.addStretch()
        settings_layout.addLayout(options_layout)
        
        settings_group.setLayout(settings_layout)
        layout.addWidget(settings_group)
        
        # 进度条
        self.progress_bar = QProgressBar()
        self.progress_bar.setVisible(False)
        layout.addWidget(self.progress_bar)
        
        # 生成按钮
        button_layout = QHBoxLayout()
        self.generate_btn = QPushButton("生成API概览")
        self.generate_btn.clicked.connect(self.generate_api_overview)
        self.generate_btn.setStyleSheet("background-color: #2196F3;")
        
        self.copy_btn = QPushButton("复制到剪贴板")
        self.copy_btn.clicked.connect(self.copy_to_clipboard)
        self.copy_btn.setEnabled(False)
        
        self.save_btn = QPushButton("保存为文档")
        self.save_btn.clicked.connect(self.save_api_documentation)
        self.save_btn.setEnabled(False)
        
        button_layout.addWidget(self.generate_btn)
        button_layout.addWidget(self.copy_btn)
        button_layout.addWidget(self.save_btn)
        layout.addLayout(button_layout)
        
        # 输出区域
        output_group = QGroupBox("API概览输出")
        output_layout = QVBoxLayout()
        
        # 输出格式选择
        format_layout = QHBoxLayout()
        format_layout.addWidget(QLabel("输出格式:"))
        self.format_combo = QComboBox()
        self.format_combo.addItems(["简洁格式", "详细格式", "Markdown格式"])
        format_layout.addWidget(self.format_combo)
        format_layout.addStretch()
        output_layout.addLayout(format_layout)
        
        self.output_text = QTextEdit()
        self.output_text.setPlaceholderText("API概览将显示在这里...")
        output_layout.addWidget(self.output_text)
        
        output_group.setLayout(output_layout)
        layout.addWidget(output_group)
        
        # 统计信息
        self.stats_label = QLabel("就绪")
        self.stats_label.setStyleSheet("color: #666; font-size: 9pt; padding: 5px;")
        layout.addWidget(self.stats_label)
        
        self.setLayout(layout)
    
    def choose_project_dir(self):
        folder = QFileDialog.getExistingDirectory(self, "选择项目目录")
        if folder:
            self.dir_input.setText(folder)
    
    def generate_api_overview(self):
        project_dir = self.dir_input.text()
        if not project_dir or not os.path.exists(project_dir):
            QMessageBox.warning(self, "警告", "请选择有效的项目目录！")
            return
        
        # 禁用按钮，显示进度条
        self.generate_btn.setEnabled(False)
        self.copy_btn.setEnabled(False)
        self.save_btn.setEnabled(False)
        self.progress_bar.setVisible(True)
        self.progress_bar.setValue(0)
        self.stats_label.setText("正在分析项目结构...")
        self.output_text.clear()
        
        # 创建并启动分析线程
        self.analyzer_thread = APIAnalyzerThread(
            project_dir,
            self.lang_combo.currentText(),
            self.include_functions.isChecked(),
            self.include_classes.isChecked(),
            self.include_methods.isChecked(),
            self.include_docstrings.isChecked()
        )
        
        self.analyzer_thread.progress_signal.connect(self.update_progress)
        self.analyzer_thread.log_signal.connect(self.add_log)
        self.analyzer_thread.finished_signal.connect(self.analysis_finished)
        self.analyzer_thread.start()
    
    def update_progress(self, value):
        self.progress_bar.setValue(value)
    
    def add_log(self, message):
        self.output_text.append(f"[INFO] {message}")
        # 滚动到底部
        cursor = self.output_text.textCursor()
        cursor.movePosition(cursor.End)
        self.output_text.setTextCursor(cursor)
    
    def analysis_finished(self, api_data):
        # 恢复UI状态
        self.generate_btn.setEnabled(True)
        self.progress_bar.setVisible(False)
        
        # 检查是否有错误
        if "error" in api_data:
            self.stats_label.setText(f"分析失败: {api_data['error']}")
            return
        
        # 格式化输出
        formatted_output = self.format_api_output(api_data)
        self.output_text.setPlainText(formatted_output)
        
        # 启用功能按钮
        self.copy_btn.setEnabled(True)
        self.save_btn.setEnabled(True)
        
        # 更新统计信息
        stats = self.calculate_stats(api_data)
        self.stats_label.setText(stats)
        
        QMessageBox.information(self, "完成", "API概览生成完成！")
    
    def format_api_output(self, api_data):
        """根据选择的格式格式化API输出"""
        format_type = self.format_combo.currentText()
        
        if format_type == "简洁格式":
            return self.format_concise(api_data)
        elif format_type == "详细格式":
            return self.format_detailed(api_data)
        elif format_type == "Markdown格式":
            return self.format_markdown(api_data)
        
        return self.format_concise(api_data)
    
    def format_concise(self, api_data):
        """简洁格式输出"""
        output = []
        output.append(f"项目: {api_data['project_name']}")
        output.append("=" * 50)
        
        for module in api_data["modules"]:
            output.append(f"\n模块: {module['name']}")
            
            if module["functions"]:
                output.append("  函数:")
                for func in module["functions"]:
                    return_str = f" -> {func['return_type']}" if func.get('return_type') else ""
                    output.append(f"    - {func['name']}({func['args']}){return_str}")
                    if self.include_docstrings.isChecked() and func.get('docstring'):
                        # 简化文档字符串，只取第一行
                        doc_lines = func['docstring'].strip().split('\n')
                        first_line = doc_lines[0].strip()
                        output.append(f"        # {first_line}")
            
            if module["classes"]:
                output.append("  类:")
                for cls in module["classes"]:
                    output.append(f"    - {cls['name']}")
                    if self.include_docstrings.isChecked() and cls.get('docstring'):
                        # 简化文档字符串，只取第一行
                        doc_lines = cls['docstring'].strip().split('\n')
                        first_line = doc_lines[0].strip()
                        output.append(f"        # {first_line}")
                    
                    if self.include_methods.isChecked() and cls["methods"]:
                        output.append("      方法:")
                        for method in cls["methods"]:
                            return_str = f" -> {method['return_type']}" if method.get('return_type') else ""
                            output.append(f"        * {method['name']}({method['args']}){return_str}")
                            if self.include_docstrings.isChecked() and method.get('docstring'):
                                # 简化文档字符串，只取第一行
                                doc_lines = method['docstring'].strip().split('\n')
                                first_line = doc_lines[0].strip()
                                output.append(f"            # {first_line}")
        
        return "\n".join(output)
    
    def format_detailed(self, api_data):
        """详细格式输出"""
        output = []
        output.append(f"项目API文档: {api_data['project_name']}")
        output.append("=" * 60)
        
        for module in api_data["modules"]:
            output.append(f"\n模块: {module['name']}")
            output.append("-" * 40)
            
            if module["functions"]:
                output.append("\n函数列表:")
                for func in module["functions"]:
                    output.append(f"\n  {func['name']}({func['args']})")
                    if func.get('return_type'):
                        output.append(f"    返回类型: {func['return_type']}")
                    if self.include_docstrings.isChecked() and func.get('docstring'):
                        output.append(f"    功能: {func['docstring']}")
            
            if module["classes"]:
                output.append("\n类列表:")
                for cls in module["classes"]:
                    output.append(f"\n  {cls['name']}")
                    if self.include_docstrings.isChecked() and cls.get('docstring'):
                        output.append(f"    描述: {cls['docstring']}")
                    
                    if self.include_methods.isChecked() and cls["methods"]:
                        output.append("    方法:")
                        for method in cls["methods"]:
                            output.append(f"      {method['name']}({method['args']})")
                            if method.get('return_type'):
                                output.append(f"        返回类型: {method['return_type']}")
                            if self.include_docstrings.isChecked() and method.get('docstring'):
                                output.append(f"        功能: {method['docstring']}")
        
        return "\n".join(output)
    
    def format_markdown(self, api_data):
        """Markdown格式输出"""
        output = []
        output.append(f"# {api_data['project_name']} - API文档")
        output.append("")
        
        for module in api_data["modules"]:
            output.append(f"## {module['name']}")
            output.append("")
            
            if module["functions"]:
                output.append("### 函数")
                output.append("")
                for func in module["functions"]:
                    return_str = f" -> {func['return_type']}" if func.get('return_type') else ""
                    output.append(f"#### `{func['name']}({func['args']}){return_str}`")
                    output.append("")
                    if self.include_docstrings.isChecked() and func.get('docstring'):
                        output.append(f"{func['docstring']}")
                    output.append("")
            
            if module["classes"]:
                output.append("### 类")
                output.append("")
                for cls in module["classes"]:
                    output.append(f"#### `{cls['name']}`")
                    output.append("")
                    if self.include_docstrings.isChecked() and cls.get('docstring'):
                        output.append(f"{cls['docstring']}")
                        output.append("")
                    
                    if self.include_methods.isChecked() and cls["methods"]:
                        output.append("**方法:**")
                        output.append("")
                        for method in cls["methods"]:
                            return_str = f" -> {method['return_type']}" if method.get('return_type') else ""
                            output.append(f"- `{method['name']}({method['args']}){return_str}`")
                            if self.include_docstrings.isChecked() and method.get('docstring'):
                                output.append(f"  - {method['docstring']}")
                        output.append("")
        
        return "\n".join(output)
    
    def calculate_stats(self, api_data):
        """计算统计信息"""
        total_functions = sum(len(module["functions"]) for module in api_data["modules"])
        total_classes = sum(len(module["classes"]) for module in api_data["modules"])
        total_methods = sum(len(cls["methods"]) for module in api_data["modules"] for cls in module["classes"])
        total_modules = len(api_data["modules"])
        
        return f"分析完成: {total_modules} 个模块, {total_functions} 个函数, {total_classes} 个类, {total_methods} 个方法"
    
    def copy_to_clipboard(self):
        clipboard = QApplication.clipboard()
        clipboard.setText(self.output_text.toPlainText())
        QMessageBox.information(self, "成功", "API概览已复制到剪贴板！")
    
    def save_api_documentation(self):
        file_path, _ = QFileDialog.getSaveFileName(
            self, "保存API文档", "api_documentation.txt", 
            "Text Files (*.txt);;Markdown Files (*.md);;All Files (*)"
        )
        if file_path:
            try:
                with open(file_path, "w", encoding="utf-8") as f:
                    f.write(self.output_text.toPlainText())
                QMessageBox.information(self, "成功", f"API文档已保存到: {file_path}")
            except Exception as e:
                QMessageBox.critical(self, "错误", f"保存文件时出错: {str(e)}")

class FileTool(QWidget):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("文件扫描与恢复工具 - Professional")
        self.resize(1000, 700)
        
        # 预设管理
        self.presets = self.load_presets()
        
        # 默认屏蔽类型
        self.ignore_extensions = [".pyc", "__pycache__", ".pyo"]
        self.scan_thread = None
        self.file_tree_text = ""  # 存储树状图文本

        self.setup_ui()
        self.setup_styles()

    def setup_ui(self):
        main_layout = QVBoxLayout()
        main_layout.setSpacing(15)
        main_layout.setContentsMargins(20, 20, 20, 20)

        # 标题
        title_label = QLabel("文件扫描与恢复工具")
        title_label.setAlignment(Qt.AlignCenter)
        title_font = QFont()
        title_font.setPointSize(16)
        title_font.setBold(True)
        title_label.setFont(title_font)
        title_label.setStyleSheet("color: #2c3e50; margin-bottom: 10px;")
        main_layout.addWidget(title_label)

        # 使用选项卡组织功能
        tab_widget = QTabWidget()
        
        # 扫描选项卡
        scan_tab = QWidget()
        self.setup_scan_tab(scan_tab)
        tab_widget.addTab(scan_tab, "文件扫描")
        
        # 恢复选项卡
        restore_tab = QWidget()
        self.setup_restore_tab(restore_tab)
        tab_widget.addTab(restore_tab, "文件恢复")
        
        # 新增API概览选项卡
        api_tab = APIOverviewTab(self)
        tab_widget.addTab(api_tab, "API概览")
        
        main_layout.addWidget(tab_widget)
        
        # 状态栏
        self.status_label = QLabel("就绪")
        self.status_label.setStyleSheet("color: #666; font-size: 9pt; padding: 5px;")
        main_layout.addWidget(self.status_label)

        self.setLayout(main_layout)

    def setup_scan_tab(self, tab):
        layout = QVBoxLayout()
        
        # 预设设置分组
        preset_group = QGroupBox("预设设置")
        preset_layout = QVBoxLayout()

        # 预设选择行
        preset_select_layout = QHBoxLayout()
        preset_select_layout.addWidget(QLabel("项目预设:"))
        
        self.preset_combo = QComboBox()
        self.preset_combo.addItems(self.presets.keys())
        self.preset_combo.currentTextChanged.connect(self.on_preset_changed)
        preset_select_layout.addWidget(self.preset_combo)
        
        self.manage_preset_btn = QPushButton("管理预设")
        self.manage_preset_btn.clicked.connect(self.manage_presets)
        preset_select_layout.addWidget(self.manage_preset_btn)
        
        preset_layout.addLayout(preset_select_layout)

        # 屏蔽类型显示和编辑
        preset_layout.addWidget(QLabel("当前屏蔽的文件类型 (每行一个):"))
        self.ignore_input = QTextEdit()
        self.ignore_input.setMaximumHeight(80)
        self.ignore_input.setPlainText("\n".join(self.ignore_extensions))
        self.ignore_input.setPlaceholderText("每行输入一个要屏蔽的文件类型或模式...\n例如: .pyc, __pycache__/, *.log, dist/")
        preset_layout.addWidget(self.ignore_input)

        preset_group.setLayout(preset_layout)
        layout.addWidget(preset_group)

        # 扫描设置分组
        scan_group = QGroupBox("扫描设置")
        scan_layout = QVBoxLayout()

        # 扫描目录选择
        scan_dir_layout = QHBoxLayout()
        scan_dir_layout.addWidget(QLabel("扫描目录:"))
        self.scan_dir_input = QLineEdit()
        self.scan_dir_input.setPlaceholderText("请选择要扫描的目录...")
        scan_dir_layout.addWidget(self.scan_dir_input)
        self.scan_btn = QPushButton("浏览")
        self.scan_btn.clicked.connect(self.choose_scan_dir)
        scan_dir_layout.addWidget(self.scan_btn)
        scan_layout.addLayout(scan_dir_layout)

        # 进度条
        self.progress_bar = QProgressBar()
        self.progress_bar.setVisible(False)
        scan_layout.addWidget(self.progress_bar)

        # 日志区域
        self.log_text = QTextEdit()
        self.log_text.setMaximumHeight(100)
        self.log_text.setPlaceholderText("扫描日志将显示在这里...")
        scan_layout.addWidget(self.log_text)

        # 扫描按钮
        button_layout = QHBoxLayout()
        self.scan_generate_btn = QPushButton("开始扫描并生成文本")
        self.scan_generate_btn.clicked.connect(self.scan_and_generate)
        self.scan_generate_btn.setStyleSheet("background-color: #2196F3;")
        button_layout.addWidget(self.scan_generate_btn)
        
        self.show_tree_btn = QPushButton("显示文件树状图")
        self.show_tree_btn.clicked.connect(self.show_file_tree)
        self.show_tree_btn.setStyleSheet("background-color: #9C27B0;")
        self.show_tree_btn.setEnabled(False)  # 初始不可用，扫描完成后可用
        button_layout.addWidget(self.show_tree_btn)
        
        scan_layout.addLayout(button_layout)

        scan_group.setLayout(scan_layout)
        layout.addWidget(scan_group)

        # 文本显示区域
        text_group = QGroupBox("文件内容预览")
        text_layout = QVBoxLayout()
        self.scan_text = QTextEdit()
        self.scan_text.setPlaceholderText("扫描结果将显示在这里...")
        text_layout.addWidget(self.scan_text)
        text_group.setLayout(text_layout)
        layout.addWidget(text_group)

        tab.setLayout(layout)

    def setup_restore_tab(self, tab):
        layout = QVBoxLayout()
        
        restore_group = QGroupBox("文件恢复")
        restore_layout = QVBoxLayout()

        # 文本文件选择
        restore_file_layout = QHBoxLayout()
        restore_file_layout.addWidget(QLabel("文本文件:"))
        self.restore_file_input = QLineEdit()
        self.restore_file_input.setPlaceholderText("请选择包含文件数据的文本文件...")
        restore_file_layout.addWidget(self.restore_file_input)
        self.restore_btn = QPushButton("浏览")
        self.restore_btn.clicked.connect(self.choose_restore_file)
        restore_file_layout.addWidget(self.restore_btn)
        restore_layout.addLayout(restore_file_layout)

        # 输出目录
        output_layout = QHBoxLayout()
        output_layout.addWidget(QLabel("输出目录:"))
        self.output_dir_input = QLineEdit("recovered_files")
        output_layout.addWidget(self.output_dir_input)
        restore_layout.addLayout(output_layout)

        # 恢复按钮
        self.restore_generate_btn = QPushButton("从文本生成文件")
        self.restore_generate_btn.clicked.connect(self.restore_files)
        self.restore_generate_btn.setStyleSheet("background-color: #FF9800;")
        restore_layout.addWidget(self.restore_generate_btn)

        restore_group.setLayout(restore_layout)
        layout.addWidget(restore_group)
        
        tab.setLayout(layout)

    def setup_styles(self):
        self.setStyleSheet("""
            QWidget {
                font-family: 'Segoe UI', 'Microsoft YaHei', sans-serif;
                font-size: 10pt;
            }
            QGroupBox {
                font-weight: bold;
                border: 2px solid #cccccc;
                border-radius: 8px;
                margin-top: 1ex;
                padding-top: 10px;
            }
            QGroupBox::title {
                subcontrol-origin: margin;
                left: 10px;
                padding: 0 5px 0 5px;
            }
            QPushButton {
                background-color: #4CAF50;
                border: none;
                color: white;
                padding: 8px 16px;
                border-radius: 4px;
                font-weight: bold;
                min-width: 80px;
            }
            QPushButton:hover {
                background-color: #45a049;
            }
            QPushButton:pressed {
                background-color: #3d8b40;
            }
            QPushButton:disabled {
                background-color: #cccccc;
                color: #666666;
            }
            QTextEdit, QLineEdit, QComboBox {
                border: 1px solid #cccccc;
                border-radius: 4px;
                padding: 6px;
            }
            QLabel {
                color: #333333;
            }
            QProgressBar {
                border: 1px solid #cccccc;
                border-radius: 4px;
                text-align: center;
            }
            QProgressBar::chunk {
                background-color: #4CAF50;
                border-radius: 3px;
            }
            QTabWidget::pane {
                border: 1px solid #cccccc;
                border-radius: 4px;
            }
            QTabBar::tab {
                background-color: #f0f0f0;
                padding: 8px 16px;
                margin-right: 2px;
                border-top-left-radius: 4px;
                border-top-right-radius: 4px;
            }
            QTabBar::tab:selected {
                background-color: #4CAF50;
                color: white;
            }
        """)

    def load_presets(self):
        """加载预设配置"""
        default_presets = {
            "Python项目": [".pyc", ".pyo", "__pycache__", "*.pyc", "dist/", "build/", ".egg-info/"],
            "C++项目": [".obj", ".o", ".exe", ".dll", ".lib", ".so", ".a", "CMakeFiles/", "cmake_install.cmake", "CMakeCache.txt", "Makefile"],
            "Java项目": [".class", ".jar", ".war", ".ear", "target/"],
            "Web项目": [".min.js", ".min.css", ".map", ".log", "node_modules/", ".npm"],
            "通用项目": [".log", ".tmp", ".temp", ".DS_Store", "Thumbs.db", ".git/", ".svn/"]
        }
        
        # 这里可以添加从文件加载预设的逻辑
        # 暂时返回默认预设
        return default_presets

    def save_presets(self):
        """保存预设到文件"""
        # 这里可以添加保存预设到文件的逻辑
        pass

    def on_preset_changed(self, preset_name):
        """当预设改变时更新屏蔽类型"""
        if preset_name in self.presets:
            self.ignore_extensions = self.presets[preset_name]
            # 更新显示
            self.ignore_input.setPlainText("\n".join(self.ignore_extensions))

    def manage_presets(self):
        """打开预设管理对话框"""
        dialog = PresetDialog(self.presets, self)
        if dialog.exec() == QDialog.Accepted:
            # 更新预设下拉框
            self.preset_combo.clear()
            self.preset_combo.addItems(self.presets.keys())
            # 保存预设
            self.save_presets()

    def choose_scan_dir(self):
        folder = QFileDialog.getExistingDirectory(self, "选择扫描目录")
        if folder:
            self.scan_dir_input.setText(folder)
            self.status_label.setText(f"已选择目录: {folder}")

    def scan_and_generate(self):
        root_dir = self.scan_dir_input.text()
        if not root_dir or not os.path.exists(root_dir):
            QMessageBox.warning(self, "警告", "请选择有效的扫描目录！")
            return

        # 从文本框获取屏蔽类型（每行一个）
        ignore_text = self.ignore_input.toPlainText().strip()
        if ignore_text:
            self.ignore_extensions = [line.strip() for line in ignore_text.split('\n') if line.strip()]
        
        # 显示屏蔽规则
        self.log_text.clear()
        self.log_text.append("应用屏蔽规则:")
        for pattern in self.ignore_extensions:
            self.log_text.append(f"  - {pattern}")

        # 禁用按钮，显示进度条
        self.scan_generate_btn.setEnabled(False)
        self.show_tree_btn.setEnabled(False)
        self.progress_bar.setVisible(True)
        self.progress_bar.setValue(0)
        self.status_label.setText("正在扫描文件，请稍候...")

        # 创建并启动扫描线程
        self.scan_thread = ScanThread(root_dir, self.ignore_extensions)
        self.scan_thread.progress_signal.connect(self.update_progress)
        self.scan_thread.finished_signal.connect(self.scan_finished)
        self.scan_thread.log_signal.connect(self.log_text.append)
        self.scan_thread.start()

    def update_progress(self, value):
        self.progress_bar.setValue(value)

    def scan_finished(self, final_text, tree_text):
        self.scan_text.setPlainText(final_text)
        self.file_tree_text = tree_text  # 保存树状图文本
        
        # 保存文件
        output_file = "all_files_combined.txt"
        tree_file = "file_tree.txt"
        try:
            with open(output_file, "w", encoding="utf-8") as f:
                f.write(final_text)
            with open(tree_file, "w", encoding="utf-8") as f:
                f.write(tree_text)
            self.status_label.setText(f"扫描完成！结果已保存到: {output_file} 和 {tree_file}")
        except Exception as e:
            self.status_label.setText(f"保存文件时出错: {str(e)}")
        
        # 恢复UI状态
        self.scan_generate_btn.setEnabled(True)
        self.show_tree_btn.setEnabled(True)  # 扫描完成后启用树状图按钮
        self.progress_bar.setVisible(False)
        
        file_count = final_text.count('=====FILE_START:')
        QMessageBox.information(self, "完成", 
                              f"文件扫描完成！\n共处理了 {file_count} 个文件。\n\n文件树状图也已生成，可以点击'显示文件树状图'按钮查看。")

    def show_file_tree(self):
        """显示文件树状图对话框"""
        if not self.file_tree_text:
            QMessageBox.information(self, "提示", "请先执行扫描以生成文件树状图。")
            return
        
        dialog = FileTreeDialog(self.file_tree_text, self)
        dialog.exec()

    def choose_restore_file(self):
        file_path, _ = QFileDialog.getOpenFileName(
            self, "选择文本文件", "", 
            "Text Files (*.txt);;All Files (*)"
        )
        if file_path:
            self.restore_file_input.setText(file_path)
            self.status_label.setText(f"已选择文件: {os.path.basename(file_path)}")

    def restore_files(self):
        input_file = self.restore_file_input.text()
        output_root = self.output_dir_input.text() or "recovered_files"
        
        if not input_file or not os.path.exists(input_file):
            QMessageBox.warning(self, "警告", "请选择有效的文本文件！")
            return

        try:
            # 显示进度
            self.restore_generate_btn.setEnabled(False)
            self.status_label.setText("正在恢复文件...")

            files_restored = self.restore_files_from_text(input_file, output_root)
            
            self.status_label.setText(f"恢复完成！共恢复了 {files_restored} 个文件到: {output_root}")
            QMessageBox.information(self, "完成", 
                                  f"文件恢复完成！\n共恢复了 {files_restored} 个文件到目录:\n{output_root}")
            
        except Exception as e:
            self.status_label.setText(f"恢复文件时出错: {str(e)}")
            QMessageBox.critical(self, "错误", f"恢复文件时出现错误:\n{str(e)}")
        finally:
            self.restore_generate_btn.setEnabled(True)

    def restore_files_from_text(self, input_file, output_root):
        if not os.path.exists(output_root):
            os.makedirs(output_root)

        current_file = None
        content_lines = []
        files_restored = 0

        with open(input_file, "r", encoding="utf-8") as f:
            for line in f:
                line_strip = line.strip()
                if line_strip.startswith("=====FILE_START:"):
                    if current_file:
                        self.write_file(current_file, content_lines)
                        files_restored += 1
                        content_lines = []
                    relative_path = line_strip[len("=====FILE_START:"):].replace("=====", "").strip()
                    current_file = os.path.join(output_root, relative_path)
                elif line_strip == "=====FILE_END=====":
                    if current_file:
                        self.write_file(current_file, content_lines)
                        files_restored += 1
                        current_file = None
                        content_lines = []
                else:
                    if current_file is not None:
                        content_lines.append(line)

        # 处理最后一个文件
        if current_file and content_lines:
            self.write_file(current_file, content_lines)
            files_restored += 1

        return files_restored

    def write_file(self, file_path, lines):
        os.makedirs(os.path.dirname(file_path), exist_ok=True)
        with open(file_path, "w", encoding="utf-8") as f:
            f.writelines(lines)


if __name__ == "__main__":
    app = QApplication([])
    
    # 设置应用程序样式
    app.setStyle('Fusion')
    
    window = FileTool()
    window.show()
    app.exec()